/**

Uebung 6 - Drehimpuls und Drehbewegungen
==========================================
Schwerpunktsatz: 
System bewegt sich so, wie wenn alle Massenpunkte im Schwerpunkt konzentriert werden
und alle äusseren Kräfte dort angreifen

Impuls - und Schwerpunktsatz:
Impuls des Systems = Vektorsumme der Impulse der Massenpunkte: p = p1 + ... + pn = m1*v1 + .... mn*vn
--> Impuls vom System = Gesamtimpuls

Folgerunng: p = m * vs  (vs: Geschwindigkeit des Schwerpunktes)
Impuls des Systems entspricht Gesammtmasse m multipliziert mit Geschwindigkeit vs des Schwerpunktes
(Anwendung des Schwerpunktsatzes: Für die Berechung des Gesamtimpulses können alle Massenpunkte im Schwerpunkt konzentriert werden)	

Impulssatz: zeitliche Aenderung des Gesamtimpulses p entspricht der Vektorsumme aller äusseren Kräfte, die auf die Massenpunkte einwirken
(Angriffspunkte der Kräfte spielen für Gesamtimpuls keine Rolle)

Impulserhaltung: Wenn die Summe der äusseren Kräfte = 0 --> Impuls p zeitlich Konstant

Drehimpuls und Drehbewgungen
----------------------------
Drehimpuls des Systems: definiert als Vektorsumme der Drehimpulse der Massenpunkte
Im Gegensatz zum Impuls kann Drehimpuls nicht einfach mit Schwerpunkt beschrieben werden weil:

Drehimpuls hängt von der Drehung des Körpers um den Schwerpunkt ab:
Mögliche Arten von Drehbewegungen
	-Drehumg um eine raumfeste Achse: Rotator
	
	Alle Punkte des Körpers bewegen sich auf  Kreisbahnen mit derselben Winkelgeschwindigkeit w
	Winkelgeschwindigkeits-Vektor in Richtung der Drehachse --> Drehsinn gemäss Rechter-Hand-Regel

	Hauptachse eines Körpers:

	Gerade a die durch Schwerpunkt eines starren Körpers geht heisst Hauptachse, wenn der Körper frei um die
	Achse rotieren kann ohne das Lagerkräfte erforderlich sind - Körper ist dynamisch ausgewuchtet d.h.
	Zentrifugalkraft und Drehmomente der einzelnen Massenpunkte heben sich auf

	-Drehum um einen raumfesten Punkt: Kreisel:
	Für jede Kreiselbewegung kann man zeigen --> zu jedem Zeitpunkt existiert eine momentane Drehachse und eine momentane Winkelgeschwindigkeit
	dadurch wie beim Rotator:
		Jeder Punkt des Körpers besitzt momentan dieselbe Geschwindigkeit (Richtung und Betrag), wie beim Rotator
		Jeder Punkt des Körpers bewegt sich momentan wie beim Rotator mit Winkelgeschwindigkeit w
		
	bei Gyroskop 2 mögliche Arten von Bewegungen





 */

package gyroscope2;

import gui.DoubleProperty;
import gui.IUebung;

import javax.media.opengl.*;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;


import com.sun.opengl.util.FPSAnimator;
import com.sun.opengl.util.GLUT;

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
public class Gyroscope 
       implements  GLEventListener, KeyListener, IUebung
{	
	private GLCanvas canvas;		//OpenGL-Canvas
	private FPSAnimator anim;
	private GLUT glut = new GLUT();
	private double xmin = -0.6;
	private double xmax = 0.6;
	private double ymin	;
	private double ymax;
	//viewing volumen vergroessen, heist clipping bereich vergroessen
	double zmin = -100, zmax = 100; // Clipping Bereich

	
	double elev = 20; // Elevation Kamera-System
	double azim = 30; // Azimut Kamera-System	
	
	private DoubleProperty elevproperty = new DoubleProperty("Elevation", -360d, 360d, 0.1d, 20d);
	private DoubleProperty azimproperty = new DoubleProperty("Azimut", -180d, 180d, 0.1d, 30d);
	
	
	//Zeitschritt
	private double dt = 2;

	private double c_radius = 0.09;
	private double c_height = 0.025;
	
	private double t = -10;
	
    double dtp = 0, dto=0;
    //wo: Winkelgeschwindigkeit der Rotation um die Achse des Rades
    //wp: Winkelgeschwindigkeit der Praezession
    //a: halber Öffnungswinkel
    double wo = -4, wp = 0.5, a = 10;
    
    volatile boolean praezession = false;
    volatile boolean lightning = false;
	    
	//  ------------------  Methoden  --------------------	
		
	void zeichneRotor(GL gl){
		//Drehung um 90 um die Y-Achse --> Z Achse entspricht der X-Achse
		//X - Achse zeigt nach hinten
		gl.glRotated(90, 0, 1, 0);
		//Gerüst auf richtige Höhe
		gl.glTranslated(0, 0.2, 0);
		if (praezession){
			/** 
			 	Drehachse beim Kreisel nicht fix --> sondern taumelt
				allgemeine Bewegung setzt sich aus 2 Drehungen zusammen
				Rad rotiert um seine Achse und diese rotiert auf einem Kegel-mantel
				um eine Konstante Richtung = Präzession
			
			**/
			gl.glRotated(dtp * wp, 0, 0, 1);
			gl.glRotated(a, 1, 0, 0);
		}
		if (!praezession){
			//Zeichnet Gegengewicht
			gl.glPushMatrix();
			gl.glColor3d(0.5, 0.5, 0.5);
			gl.glTranslated(0,0,-0.15);
			glut.glutSolidCube(0.05f);
			gl.glPopMatrix();
		}
		//Achse, besteht aus 3 Teilen
		gl.glColor3d(0.54, 0.27, 0.07);
		//Achse nach links
		glut.glutSolidCylinder(c_radius/16, -6*c_height, 15, 10);
		//Achse nach rechts
		glut.glutSolidCylinder(c_radius/16, 6*c_height, 15, 10);
		gl.glTranslated(0, 0, 6*c_height);
		//Achse mit Spitz für Befestigung
		glut.glutSolidCylinder(c_radius/16, 2*c_height, 15, 10);
		//Drehung des Rotors
		gl.glRotated(dto * wo, 0, 0, 1);
		gl.glColor3d(0.5, 0.5, 0.5);
		//Rotor
		glut.glutSolidCylinder(c_radius, c_height, 15, 10);
		//Befestigung des Rotors
		glut.glutWireCone(c_radius/4, 3*c_height, 15, 10);
		
		if (t==360)t = 0;
		t+=-dt;
	}
	
	//Zeichnet Aufhängung: Rotor dreht such um diesen Raumfesten Punkt
	void zeichneMount(GL gl){
		gl.glPushMatrix();
		gl.glColor3d(0.5, 0.5, 0.5);
		gl.glRotated(90, 1, 0, 0);
		glut.glutSolidCylinder(c_radius, 0.01, 15, 10);
		gl.glColor3d(0.54, 0.27, 0.07);
		glut.glutSolidCylinder(c_radius/16, -0.2, 15, 10);
		gl.glPopMatrix();
	}
			

	 		
	// Koordinatenachsen zeichnen
	void zeichneAchsen(GL gl) {
		gl.glBegin(GL.GL_LINES);
		// x-Achse
		gl.glColor3d(1.0, 0.0, 0.0);
		gl.glVertex3d(xmin/2, 0,0);
		gl.glVertex3d(xmax/2, 0,0);
		// y-Achse
		gl.glColor3d(0.0, 0.0, 1.0);
		gl.glVertex3d(0, -ymax/2,0);
		gl.glVertex3d(0, ymax/2,0);
		// z-Achse
		gl.glColor3d(0.0, 1.0, 0.0);
		gl.glVertex3d(0, 0,0);
		gl.glVertex3d(0, 0,0.2);
						
		gl.glEnd();
		
	}



     // Konstruktor
     public Gyroscope()
     {  
    	Frame f = new Frame("Gyroscope");
        f.setSize(1280, 960);
//      f.addWindowListener(this);
        // OpenGL-Window
        canvas = new GLCanvas();
        canvas.addKeyListener(this);
        canvas.addGLEventListener(this);
        f.add(canvas);
        f.setVisible(true);
        canvas.requestFocus();
     }
     
     
     public Gyroscope(GLJPanel jp){
         // OpenGL-Window
//         f.addWindowListener(this);
         jp.addKeyListener(this);
         jp.addGLEventListener(this);
         jp.setVisible(true);
     }
     

     public static void main(String[] args)                     // main-Methode der Applikation
     {  new Gyroscope();
     }


     //  ---------  OpenGL-Events  -----------------------

	public void init(GLAutoDrawable drawable) {
		//OpenGL-Objekt
		GL gl = drawable.getGL();
		gl.glClearColor(0.68f, 0.84f, 0.9f, 1.0f); // erasing color
		
		//aktivieren des z Buffers
		gl.glEnable(GL.GL_DEPTH_TEST);
		//aktivieren des Polygon Offset
		gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
		//zu den Z-Werten wir ein Offset hinzugefuegt, so das die Z Werte nicht die gleichen Werte haben
		gl.glPolygonOffset(1,1);
		
		anim = new FPSAnimator(drawable, 60, true);
		anim.start();
	     
	}
	
    void initLighting(GL gl)                        
    {
       gl.glEnable(GL.GL_LIGHTING);                       // Beleuchtung aktivieren
       gl.glEnable(GL.GL_NORMALIZE);                      // Normalenvektoren automatisch normieren
       gl.glShadeModel(GL.GL_SMOOTH);                     // keine Farb-Interpolationen zwischen Vertices
       float[] amb = { 0.5f, 0.9f, 0.5f, 1.0f };          // Streulicht (RGBA-Werte)
       gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, amb, 0);

       //  -------  Lichtquelle  ------------
       gl.glEnable(GL.GL_LIGHT0);                         // Lichtquelle0 aktivieren
       float[] lightPos = { -10.0f, 5.0f, 10.0f, 1.0f };  // Koordinaten der Lichtquelle (homogene Koord.)
       gl.glLightfv(GL.GL_LIGHT0,                         // Lichtquelle positionieren
                     GL.GL_POSITION, lightPos, 0);
       
   //  -------  Lichtquelle 2  ------------
       gl.glEnable(GL.GL_LIGHT1);
       //Add directed light
       float lightColor1[] = {0.2f, 0.9f, 0.2f, 1.0f}; //Color (0.5, 0.2, 0.2)
       //Coming from the direction (-1, 0.5, 0.5)
       float lightPos1[] = { -10.0f, 5.0f, 10.0f, 1.0f };
       gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, lightColor1, 1);
       gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightPos1, 1);
    }
    
    void initMaterial(GL gl,          
            float amb,           // Reflexion Streulicht 0.0 .. 1.0
            float diff,          // diffuse Reflexion 0.0 .. 1.0
            float spec,          // spiegelnde Reflexion 0.0 .. 1.0
            int specExponent)    // Exponent spiegelnde Reflexion 0 .. 128
	{  
		 float[] ambiRGB = { amb, amb, amb, 1 };          // ambiente Reflexion (RGBA-Werte)
		 float[] diffRGB = { diff, diff, diff, 1 };       // diffuse Relexion (RGBA-Werte)
		 float[] specRGB = { spec, spec, spec, 1 };       // spiegelnde Reflexion (RGBA-Werte)
		 float[] specExp = { specExponent };              // Exponent spiegelnde Reflexion
		 gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, ambiRGB, 0);
		 gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, diffRGB, 0);
		 gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, specRGB, 0);
		 gl.glMaterialfv(GL.GL_FRONT, GL.GL_SHININESS, specExp, 0);
	}


	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		if (lightning){initLighting(gl);initMaterial(gl, 0.4f, 0.4f, 0.4f, 2);}
				
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
		// Kamera-System positionieren:
		// Kamera vom System weg bewegen, hat aber fuer die orthogonale
		// Projektion keine Wirkung, erst wenn die eleveation dazu kommt spielt
		// dies eine Rolle
		gl.glTranslated(0, 0, 0.1);
		// wichtig elevation nach dem translate;
		// notizen: ensrpicht der 1 Variante: absolute Variante
		gl.glRotated(elevproperty.getCurrent(), 1, 0, 0); // Drehung um x-Achse
		gl.glRotated(-azimproperty.getCurrent(), 0, 1, 0); // Drehung um y-Achse

		// Achsen sollen nicht beleuchtet sein
		gl.glColor3d(0, 0, 0.1);

		// Hier Position der Kamera, muss die Koordinaten in einem Float Array
		// vorbereiten
		float[] pos = { 1000, 1000, -1000, 1 };
		// Die Koordinaten des Lichts werden in das Kamerasystem umgerechnet
		// bewege ich nur die Kamera, dann haben wir eine andere Beleuchtung
		gl.glLightfv(gl.GL_LIGHT0, GL.GL_POSITION, pos, 0);
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
		
		zeichneAchsen(gl);
		zeichneMount(gl);
		zeichneRotor(gl);
		++dtp;
		++dto;
	}

	//Window resized
     public void reshape(GLAutoDrawable drawable,
                         int x, int y,
                         int width, int height)
     {  
    	 GL gl = drawable.getGL();
     //damit keine Verzerrungen enstehen
     	ymax = xmax * height / width;
     	ymin = -ymax;
     	gl.glMatrixMode(GL.GL_PROJECTION);
     	gl.glLoadIdentity();
        gl.glOrtho(xmin, xmax, ymin, ymax, zmin, zmax);
        gl.glViewport(0, 0, width, height);
     }

     public void displayChanged(GLAutoDrawable drawable,
                                boolean modeChanged,
                                boolean deviceChanged)
     { }


     //  ---------  Window-Events  --------------------

     public void windowClosing(WindowEvent e)
     {  System.exit(0);
     }
     public void windowActivated(WindowEvent e) {  }
     public void windowClosed(WindowEvent e) {  }
     public void windowDeactivated(WindowEvent e) {  }
     public void windowDeiconified(WindowEvent e) {  }
     public void windowIconified(WindowEvent e) {  }
     public void windowOpened(WindowEvent e) {  }

 	
	
//  ---------  Key Events  --------------------

	@Override
	public void keyPressed(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_S:
				if (anim.isAnimating()) {
					anim.stop();
				} else {
					anim.start();
				}
			break;
		case KeyEvent.VK_UP:
			elev++;
			canvas.repaint();
			break;
		case KeyEvent.VK_DOWN:
			elev--;
			canvas.repaint();
			break;
		case KeyEvent.VK_LEFT:
			azim++;
			canvas.repaint();
			break;
		case KeyEvent.VK_RIGHT:
			azim--;
			canvas.repaint();
			break;
		case KeyEvent.VK_R:
			break;
		case KeyEvent.VK_N:
			break;
		case KeyEvent.VK_M:
			break;
		case KeyEvent.VK_P:
			praezession=true;
			break;
		default:
			break;
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {}

	@Override
	public void keyTyped(KeyEvent e) {}
	
	@Override
	public void start() {
		if (!anim.isAnimating()){
			anim.start();			
		}
	}

	@Override
	public void stop() {
		if (anim.isAnimating()){
			anim.stop();
		}	
	}

	public void togglePrecision(){
		praezession=!praezession;
	}
	
	public void toggleLightning(){
		lightning=!lightning;
	}
	public void modeSwtich(int mode) {
		// TODO Auto-generated method stub
		
	}

	public DoubleProperty[] getProperties() {
		DoubleProperty[] res = {elevproperty,azimproperty};
		return res;
	}
  }